import { RunTimeLayoutConfig } from '@@/plugin-layout/layoutExports';
import Footer from '@/components/Footer';
import { history } from 'umi';
import RightContent from '@/components/RightContent';
import detectEthereumProvider from '@metamask/detect-provider';
import { notification } from 'antd';
import { ethers } from 'ethers';
import { getAccount } from '@/services/aqchain-antd/account';
import { RequestConfig } from '@@/plugin-request/request';
import { ResponseError } from 'umi-request';

export declare type Contracts = {
  testContract: ethers.Contract;
  fileStorageContract: ethers.Contract;
  saleContract: ethers.Contract;
  auctionContract: ethers.Contract;
  licenseContract: ethers.Contract;
  tokenContract: ethers.Contract;
  exchangeContract: ethers.Contract;
};

export declare type InitialState = {
  provider: ethers.providers.Web3Provider;
  contracts: Contracts;
  currentAccount?: API.ModelsAccount;
};

/**
 * @see  https://umijs.org/zh-CN/plugins/plugin-initial-state
 * */
export async function getInitialState(): Promise<InitialState | undefined> {
  try {
    // 检查钱包
    const ethereum = (await detectEthereumProvider({
      mustBeMetaMask: true,
    })) as any;

    // 检查区块链钱包是否安装
    if (ethereum) {
      console.log('已安装MetaMask钱包!');
    } else {
      // todo 处理未安装钱包流程
      throw new Error('未安装MetaMask钱包');
    }

    // 监听钱包事件 账户和链发生修改后进行重新进行初始检查
    ethereum.on('accountsChanged', function (accounts: any) {
      console.log(accounts);
      window.location.reload();
    });
    ethereum.on('chainChanged', (chainId: number) => {
      console.log(chainId);
      window.location.reload();
    });

    // 使用ethers处理
    const provider = new ethers.providers.Web3Provider(ethereum);
    // 检查链ID
    const { chainId } = await provider.getNetwork();
    if (chainId !== 9999) {
      throw new Error('未连接正确的区块链网络');
    }

    // 合约检查
    const testABI = [
      'function test(string memory fileHash,address creator,string memory tokenURI,bytes memory signature) public view returns (bool)',
    ];

    const fileStorageABI = [
      // Read-Only Functions
      'function test(string memory fileHash,address creator,string memory tokenURI,bytes memory signature) public view returns (bool)',
      'function ownerOf(uint256 tokenId) public view returns (address)',
      'function getTokenId(string memory fileHash) public view returns (uint256)',
      'function tokenURI(uint256 tokenId) public view returns (string memory)',
      'function getApproved(uint256 tokenId) public view returns (address)',
      // Authenticated Functions
      'function create(string memory fileHash,address creator,string memory tokenURI,bytes memory signature) public',
      'function approve(address to,uint256 tokenId)',
      // Events
      'event Transfer(address indexed from, address indexed to, uint256 indexed tokenId)',
    ];

    const saleABI = [
      // Read-Only Functions
      'function getSaller(uint256 tokenId) external view returns(address)',
      'function getPrice(uint256 tokenId) external view returns(uint256)',
      'function getBuyer(uint256 tokenId) external view returns(address)',
      // Authenticated Functions
      'function create(uint256 tokenId,uint256 price,address buyer) external',
      'function cancel(uint256 tokenId) external',
      'function confirm(uint256 tokenId) external payable',
      // Events
    ];

    const auctionABI = [
      // Read-Only Functions
      'function getSeller(uint256 tokenId) external view returns(address)',
      'function getLowestPrice(uint256 tokenId) external view returns(uint256)',
      'function getEnd(uint256 tokenId) external view returns(uint256)',
      'function getHighestBidder(uint256 tokenId) external view returns(address)',
      'function getHighestBid(uint256 tokenId) external view returns(uint256)',
      // Authenticated Functions
      'function create(uint256 tokenId,uint256 price,uint256 duration) external',
      'function bid(uint256 tokenId) external payable',
      'function finish(uint256 tokenId) external',
      // Events
    ];

    const licenseABI = [
      // Read-Only Functions
      // Authenticated Functions
      'function create(uint256 tokenId,uint256 price,uint256 maxCount) external',
      'function finish(uint256 tokenId) external',
      'function buy(uint256 tokenId) external payable returns(uint256)',
      // Events
    ];

    const tokenABI = [
      // Read-Only Functions
      // Authenticated Functions
      'function test() public',
      // Events
    ];

    const exchangeABI = [
      // Read-Only Functions
      'function min() public view returns(uint256)',
      // Authenticated Functions
      'function currencyToEth(address payable account) public payable',
      'function ethToCurrency(string memory orderNo) public payable',
    ];
    const test = '0xe6B23EcaCB69AfF36b5364518A1f86CF269A0988';
    const fileStorageAddress = '0x3A220f351252089D385b29beca14e27F204c296A';
    const saleAddress = '0xdB7d6AB1f17c6b31909aE466702703dAEf9269Cf';
    const auctionAddress = '0xcBa37ECfA8029838FEBD83b6a877F0e7f1f13b3E';
    const licenseAddress = '0x880EC53Af800b5Cd051531672EF4fc4De233bD5d';
    const tokenAddress = '0x3Dc2cd8F2E345951508427872d8ac9f635fBe0EC';
    const exchangeAddress = '0xe213D8b68cA3d01e51a6dBA669De59AC9A8359eE';
    const testContract = new ethers.Contract(test, testABI, provider);
    const fileStorageContract = new ethers.Contract(
      fileStorageAddress,
      fileStorageABI,
      provider,
    );
    const saleContract = new ethers.Contract(saleAddress, saleABI, provider);
    const auctionContract = new ethers.Contract(
      auctionAddress,
      auctionABI,
      provider,
    );
    const licenseContract = new ethers.Contract(
      licenseAddress,
      licenseABI,
      provider,
    );
    const tokenContract = new ethers.Contract(tokenAddress, tokenABI, provider);
    const exchangeContract = new ethers.Contract(
      exchangeAddress,
      exchangeABI,
      provider,
    );

    const contracts: Contracts = {
      testContract,
      fileStorageContract,
      saleContract,
      auctionContract,
      licenseContract,
      tokenContract,
      exchangeContract,
    };

    // 检查钱包与当前网站连接的账户 返回数组长度为0时代表没有账户与app连接
    const accounts = await provider.listAccounts();

    if (accounts.length > 0) {
      // 获取钱包当前默认选择的账户
      const address = await provider.getSigner().getAddress();
      // 获取钱包账户信息
      const response = await getAccount({ address });
      if (response.success) {
        const currentAccount = response.data;
        return {
          provider,
          contracts,
          currentAccount,
        };
      }
    }
    return {
      provider,
      contracts,
    };
  } catch (e) {
    console.error('钱包初始化失败 ' + e);
    return;
  }
}

const codeMessage = new Map([
  [200, '服务器成功返回请求的数据。'],
  [201, '新建或修改数据成功。'],
  [202, '一个请求已经进入后台排队（异步任务）。'],
  [204, '删除数据成功。'],
  [400, '发出的请求有错误，服务器没有进行新建或修改数据的操作。'],
  [401, '用户没有权限（令牌、用户名、密码错误）。'],
  [403, '用户得到授权，但是访问是被禁止的。'],
  [404, '发出的请求针对的是不存在的记录，服务器没有进行操作。'],
  [406, '请求的格式不可得。'],
  [410, '请求的资源被永久删除，且不会再得到的。'],
  [422, '当创建一个对象时，发生一个验证错误。'],
  [500, '服务器发生错误，请检查服务器。'],
  [502, '网关错误。'],
  [503, '服务不可用，服务器暂时过载或维护。'],
  [504, '网关超时。'],
]);

const errorHandler = (error: ResponseError) => {
  const { response, data } = error;

  if (response && response.status) {
    const { status, statusText, url } = response;
    const requestErrorMessage = '请求错误';
    const errorMessage = `${requestErrorMessage} ${status}: ${url}`;
    const errorDescription = codeMessage.get(status) || statusText;
    notification.error({
      message: errorMessage,
      description: errorDescription + ' : ' + data.errorMessage,
    });
  }

  // if (!response) {
  //   notification.error({
  //     description: '您的网络发生异常，无法连接服务器',
  //     message: '网络异常',
  //   });
  // }

  return data;
};

export const request: RequestConfig = {
  errorHandler,
};

// ProLayout 支持的api https://procomponents.ant.design/components/layout
export const layout: RunTimeLayoutConfig = ({ initialState }) => {
  return {
    disableContentMargin: false,
    // waterMarkProps: {
    //   content: initialState?.currentUser?.name,
    // },
    rightContentRender: () => <RightContent />,
    footerRender: () => <Footer />,
    onPageChange: () => {
      const { location } = history;
      // 如果没有连接钱包，重定向到 index
      if (
        !initialState ||
        !initialState.provider ||
        !initialState.contracts ||
        !initialState.currentAccount
      ) {
        history.push('/index');
      }
    },
    menuHeaderRender: undefined,
    // 自定义 403 页面
    // unAccessible: <div>unAccessible</div>,
  };
};
